
#import "GameManager.h"

int W=320;
int H=480;

@implementation GameManager

#pragma mark ============================= Init Methods ================================

+ (GameManager*) getInstance  {
    
    static GameManager* gameManager;
		
	if (!gameManager) {
        gameManager = [[GameManager alloc] init];
		[gameManager preloader];            
	}
	    
    return gameManager;
}

- (void) preloader {
    sprites = [[NSMutableArray alloc] initWithCapacity:20];
    newSprites = [[NSMutableArray alloc] initWithCapacity:20];
    destroyableSprites = [[NSMutableArray alloc] initWithCapacity:20];
   
    //Preload OGL-Textures       
    [self getTex: @"fighter.png" isImage: YES];
    [self getTex: @"octo_8f.png" isImage: YES];
    [self getTex: @"explosion_8f.png" isImage: YES];
    [self getTex: @"player.png" isImage: YES];
    [self getTex: @"mine.png" isImage: YES];
    [self getTex: @"bullets.png" isImage: YES];
    [self getTex: @"gear.png" isImage: YES];
    
    //Parallax-Layer
    back = [[ParallaxLayer alloc] initWithPic: @"background.png"];
    clouds = [[ParallaxLayer alloc] initWithPic: @"clouds.png"];
    
    //Optional
    //[self getTex: @"abc" isImage: NO]; 
    
    [self setOGLProjection];
    
    state = LOAD_GAME;
}

/**
 *  恢复初始状态
 */
- (void) loadGame { 
    [sprites removeAllObjects];
    [newSprites removeAllObjects];
    [destroyableSprites removeAllObjects];      
    
    //Optional
    //[self removeFromDictionary: @"myTex1.png"];
          
    [self createSprite: PLAYER 
                 speed: CGPointMake(0, 0) 
                   pos: CGPointMake(0, 0)];
}

/**
 *  创建Sprite
 *
 *  @param type <#type description#>
 *  @param sxy  <#sxy description#>
 *  @param pxy  <#pxy description#>
 *
 *  @return <#return value description#>
 */
- (id) createSprite: (int) type 
              speed: (CGPoint) sxy 
                pos: (CGPoint) pxy {      
    if (type == PLAYER) { 
        player = [[Player alloc] initWithPic: @"player.png" 
                                    frameCnt: 1                                     
                                   frameStep: 0
                                       speed: sxy
                                         pos: pxy];     
        [player setType: PLAYER];  
        [newSprites addObject: player];
        [player release];
        return player;
    } else if (type == BULLET) {
        Bullet *bullet = [[Bullet alloc] initWithPic: @"bullets.png" 
                                            frameCnt: 1                                     
                                           frameStep: 0
                                               speed: sxy
                                                 pos: pxy];     
        [bullet setType: BULLET];  
        [newSprites addObject: bullet];
        [bullet release]; 
        return bullet;            
    } else if (type == GEAR) {
        Gear *gear = [[Gear alloc] initWithPic: @"gear.png" 
                                      frameCnt: 1                                     
                                     frameStep: 0
                                         speed: sxy
                                           pos: pxy];     
        [gear setType: GEAR];  
        [newSprites addObject: gear];
        [gear release]; 
        return gear;        
    } else if (type == OCTO) {
        Octo *octo = [[Octo alloc] initWithPic: @"octo_8f.png" 
                                      frameCnt: 8                                     
                                     frameStep: 3
                                         speed: sxy
                                           pos: pxy];     
        [octo setType: OCTO];  
        [newSprites addObject: octo];
        [octo release];
        return octo;
    } else if (type == MINE) {
        Mine *mine = [[Mine alloc] initWithPic: @"mine.png" 
                                      frameCnt: 1                                     
                                     frameStep: 0
                                         speed: sxy
                                           pos: pxy];     
        [mine setType: MINE];  
        [newSprites addObject: mine];
        [mine release];
        return mine;
    } else if (type == FIGHTER) {
        Fighter *fighter = [[Fighter alloc] initWithPic: @"fighter.png" 
                                               frameCnt: 1                                     
                                              frameStep: 0
                                                  speed: sxy
                                                    pos: pxy];     
        [fighter setType: FIGHTER];  
        [newSprites addObject: fighter];
        [fighter release];
        return fighter;    
    } else if (type == ANIMATION) {        
        Animation *ani = [[Animation alloc] initWithPic: @"explosion_8f.png"
                                               frameCnt: 8                                                 
                                              frameStep: 3
                                                  speed: sxy
                                                    pos: pxy];        
        [ani setType: ANIMATION];
        [newSprites addObject: ani];
        [ani release];  
        return ani;
    } else {
        NSLog(@"ERROR: Unknown sprite type: %i", type); 
        return nil;
    }       
}

- (void) createExplosionFor: (Sprite *) sprite {    
    CGPoint p = [Animation getOriginBasedOnCenterOf: [sprite getRect] 
                                             andPic: @"explosion_8f.png"
                                       withFrameCnt: 8];    
    [self createSprite: ANIMATION 
                 speed: CGPointMake(0, 0) 
                   pos: p];        
}

#pragma mark ============================= Game Handler ===============================

- (void) touchBegan: (CGPoint) p {    
    [self handleStates];
    if (state == PLAY_GAME && player) {
        [player setTouch: p];
    }    
}

- (void) touchMoved: (CGPoint) p {
    if (state == PLAY_GAME) {
        [self touchBegan: p];
    }    
}

- (void) touchEnded {
    if (state == PLAY_GAME && player) {
        [player touchEnded];
    }   
}

- (void) handleStates {   
    if (state == START_GAME) {
        state = PLAY_GAME;
    }
    else if (state == GAME_OVER) {
        state = LOAD_GAME;
    }
}

- (void) drawStatesWithFrame: (CGRect) frame { 
    W = frame.size.width;
    H = frame.size.height; 
    CGPoint o = [self getViewportOrigin];
    switch (state) {
        case LOAD_GAME: 
            [self loadGame];
            state = START_GAME;
            break;
        case START_GAME:             
            [self drawOGLString: @"Tap screen to start!" at: CGPointMake(o.x, o.y)];
            [self drawOGLString: @"How to control the ship:" at: CGPointMake(o.x, o.y + 50)];
            [self drawOGLString: @"Tap left - turn left." at: CGPointMake(o.x, o.y + 75)];
            [self drawOGLString: @"Tap right - turn right." at: CGPointMake(o.x, o.y + 100)];
            break;    
        case PLAY_GAME:
            [self playGame];
            break;
        case GAME_OVER:
            [self playGame];
            [self drawOGLString: @"G A M E  O V E R" at: CGPointMake(o.x, o.y)];
            break;             
        default: NSLog(@"ERROR: Unknown state: %i", state);
            break;
    }    
}	 

- (void) playGame {
    timer++;    
//    [self scrollWorld];    
    
    [back drawWithFactor: 2 
              relativeTo: [player getPos]
                atOrigin: [self getViewportOrigin]];                   
    [clouds drawWithFactor: 1
                relativeTo: [player getPos]
                  atOrigin: [self getViewportOrigin]]; 
    //    创建敌方角色
    [self generateNewEnemies];
    [self manageSprites];             
    [self renderSprites];          
} 

- (void) scrollWorld {    
    CGPoint p = [player getPos];
    CGRect r = [player getRect];
    xt = W/2 - r.size.width/2 - p.x;
    yt = H/2 - r.size.height/2 - p.y;
    glLoadIdentity();
    glTranslatef(xt, yt, 0);
}

- (CGPoint) getViewportOrigin {
    return CGPointMake(-xt, -yt);
}

- (void) generateNewEnemies {            
    if (timer % 12 == 0) {
        int sx = [self getRndBetween: -3 and: 3];
        int sy = [self getRndBetween: -7 and: -1];
        [self generateEnemy: OCTO speedX: sx speedY: sy];           
    }  
    
    if (timer % 5 == 0) {
        [self generateEnemy: MINE speedX: 0 speedY: 0];
    } 
    
    if (timer % 18 == 0) {
        [self generateEnemy: FIGHTER speedX: 7 speedY: 7];
    } 
    
    static int fighterCnt = 15; 
    static CGPoint startP; 
    if (fighterCnt == 15 && timer % 20 == 0) { 
        fighterCnt = 0;
        startP = [self getRndStartPos];
    }    
    if (timer % 9 == 0 && fighterCnt < 15) { 
        fighterCnt++;
        [self createSprite: FIGHTER 
                     speed: CGPointMake(7, 7) 
                       pos: startP]; 
    } 
}

- (void) generateEnemy: (int) type 
                speedX: (int) sx 
                speedY: (int) sy { 
    CGPoint startPos = [self getRndStartPos];
    [self createSprite: type 
                 speed: CGPointMake(sx, sy) 
                   pos: startPos];         
}

- (CGPoint) getRndStartPos {
    int px = -W, py = -H; 
    int f = 128; 
    int flag = [self getRndBetween: 0 and: 3];
    switch (flag) {              
        case 0: //Top
            px    = [self getRndBetween: -f-W and: f+W*2];
            py    = [self getRndBetween: -f-H and: -f];
            break;
        case 1: //Left
            px    = [self getRndBetween: -f-W and: -f];
            py    = [self getRndBetween: -f-H and: f+H*2];
            break;    
        case 2: //Right
            px    = [self getRndBetween: f+W and: f+W*2];
            py    = [self getRndBetween: -f-H and: f+H*2];
            break; 
        case 3: //Bottom
            px    = [self getRndBetween: -f-W and: f+W*2];
            py    = [self getRndBetween: f+H and: f+H*2];
            break;      
    }
    CGPoint o = [self getViewportOrigin];
    return CGPointMake(o.x + px, o.y + py);
}

- (void) checkSprite: (Sprite *) sprite {
    if ([sprite getType] == PLAYER || [sprite getType] == BULLET) {                
        for (Sprite *sprite2test in sprites) {        
            if ([sprite2test getType] == OCTO
                || [sprite2test getType] == FIGHTER) {              
                if ([sprite checkColWithSprite: sprite2test] && state != GAME_OVER) {                
                    [sprite hit];
                    [sprite2test hit];
                }
            }
            if ([sprite getType] == BULLET && [sprite2test getType] == MINE) {
                if ([sprite checkColWithSprite: sprite2test]) {
                    [sprite hit];
                    [[GameManager getInstance] createExplosionFor: sprite];
                }    
            }
            if ([sprite getType] == PLAYER && [sprite2test getType] == MINE) {
                if ([sprite checkColWithSprite: sprite2test]) {
                    [sprite hit];
                }    
            }
        } 
    }  
}

#pragma mark ============================= OGL Methods ===============================

- (void) setOGLProjection {
    //Set View
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity();    
    glOrthof(0, W, H, 0, 0, 1); 
    
    glMatrixMode(GL_MODELVIEW);
    glEnableClientState(GL_VERTEX_ARRAY); 
    glDisable(GL_DEPTH_TEST); //2D only
} 

- (void) drawOGLLineFrom: (CGPoint) p1 to: (CGPoint) p2 {
    GLshort vertices[ ] = { 
        p1.x, p1.y,
        p2.x, p2.y        
    };      

    glVertexPointer(2, GL_SHORT, 0, vertices);           
    glColor4f(1, 1, 0, 1);
    glDrawArrays(GL_LINES, 0, 2);       
}

- (void) drawOGLRect: (CGRect) rect {    
    GLshort vertices[ ] = {
        0,                  rect.size.height, 
        rect.size.width,    rect.size.height, 
        0,                  0, 
        rect.size.width,    0  
    };
        
    glVertexPointer(2, GL_SHORT, 0, vertices);           
    glColor4f(1, 1, 0, 1);
    
    glPushMatrix();      
    glTranslatef(rect.origin.x, rect.origin.y, 0);          
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 
    glPopMatrix();      
}

- (void) drawOGLImg: (NSString*) picName at: (CGPoint) p {    
    Tex *tex = [self getTex: picName isImage: YES]; 
    if (tex) {
        [tex drawAt: p];
    }
}

- (CGSize) getOGLImgDimension: (NSString*) picName {    
    Tex *tex = [self getTex: picName isImage: YES]; 
    if (tex) {
        return CGSizeMake([tex getWidth], [tex getHeight]);
    }
    return CGSizeMake(0, 0); 
}

- (void) drawOGLString: (NSString*) text at: (CGPoint) p {    
    Tex *tex = [self getTex: text isImage: NO]; 
    if (tex) {
        [tex drawAt: p];
    }
}

- (Tex *) getTex: (NSString*) name isImage: (bool) imgFlag { 
	Tex *tex = [[self getDictionary] objectForKey: name];
	if (!tex) {
		tex = [[Tex alloc] init];
        if (imgFlag) {            
            [tex createTexFromImage: name];             
        } else {
            [tex createTexFromString: name];
        }    
        [[self getDictionary] setObject: tex forKey: name];
        [tex release];         
	}          
	return tex;  
}

#pragma mark ============================= Helper Methods ===============================

- (void) setState: (int) stt {
    state = stt;
}

- (void) manageSprites {
    //NSLog(@"Sprites: %i destroyable: %i new: %i", [sprites count], [destroyableSprites count], [newSprites count]);
    
    for (Sprite *destroyableSprite in destroyableSprites) { 
        for (Sprite *sprite in sprites) { 
            if (destroyableSprite == sprite) { 
                [sprites removeObject: sprite];
                break;
            }
        }   
    }  
    
    for (Sprite *newSprite in newSprites){ 
        [sprites addObject: newSprite];   
    } 
    
    [destroyableSprites removeAllObjects]; 
    [newSprites removeAllObjects];
}

- (void) renderSprites {
    for (Sprite *sprite in sprites) { 
        if ([sprite isActive]) {             
            [self checkSprite: sprite];
            if ([sprite getType] != PLAYER) {
                [sprite draw]; 
            }    
        } else {
            [destroyableSprites addObject: sprite]; 
        }    
    }         

    [player draw];         
}

- (NSMutableDictionary *) getDictionary {
	if (!dictionary) { 
		dictionary = [[NSMutableDictionary alloc] init]; 
	}
	return dictionary;
}

- (void) removeFromDictionary: (NSString*) name {
    [[self getDictionary] removeObjectForKey: name];
} 

- (int) getRndBetween: (int) bottom and: (int) top {		
	int rnd = bottom + (arc4random() % (top+1-bottom)); 
	return rnd;
} 

- (void) dealloc {        
    [sprites release];
    [newSprites release];
    [destroyableSprites release];
    [dictionary release];
    [super dealloc];
}

@end
